﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="RectangleF.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2008, 2009, 2010 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.Core.
//
//   starLiGHT.Core is free software: you can redistribute it and/or modify
//   it under the terms of the GNU Lesser General Public License as published by
//   the Free Software Foundation, either version 3 of the License, or
//   (at your option) any later version.
//
//   starLiGHT.Core is distributed in the hope that it will be useful,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   GNU Lesser General Public License for more details.
//
//   You should have received a copy of the GNU Lesser General Public License
//   along with starLiGHT.Core. If not, see http://www.gnu.org/licenses/.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.Core are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 620                   $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2010-12-07 15:23:58 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Text;
    using Microsoft.Xna.Framework;

    #endregion

    /// <summary>
    /// Defines a rectangle.
    /// </summary>
    public struct RectangleF : IEquatable<RectangleF>
    {
        #region Fields
        /// <summary>
        /// Gets a RectangleF with all of its values set to zero.
        /// </summary>
        public static readonly RectangleF Empty = new RectangleF();

        private Vector2 topLeft;
        private Vector2 topRight; 
        private bool topRightDirty;
        private Vector2 bottomLeft; 
        private bool bottomLeftDirty;
        private Vector2 bottomRight;
        private Vector2 size; 
        private bool sizeDirty;
        private Vector2 center; 
        private bool centerDirty;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the RectangleF struct.
        /// </summary>
        /// <param name="x">The x-coordinate of the rectangle.</param>
        /// <param name="y">The y-coordinate of the rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        public RectangleF(float x, float y, float width, float height)
        {
            this.topLeft = new Vector2(x, y);
            this.bottomRight = this.topLeft + new Vector2(width, height);

            this.size = new Vector2();
            this.sizeDirty = true;
            this.centerDirty = true;
            this.center = new Vector2();
            this.bottomLeftDirty = true;
            this.bottomLeft = new Vector2();
            this.topRightDirty = true;
            this.topRight = new Vector2();
        }

        /// <summary>
        /// Initializes a new instance of the RectangleF struct.
        /// </summary>
        /// <param name="x">The x-coordinate of the rectangle.</param>
        /// <param name="y">The y-coordinate of the rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        /// <param name="updateAll">
        /// If set to true the Size and Corners are calculated during construction 
        /// not on access of the properties.
        /// </param>
        public RectangleF(float x, float y, float width, float height, bool updateAll)
        {
            this.topLeft = new Vector2(x, y);
            this.bottomRight = this.topLeft + new Vector2(width, height);

            this.size = new Vector2();
            this.sizeDirty = true;
            this.centerDirty = true;
            this.center = new Vector2();
            this.bottomLeftDirty = true;
            this.bottomLeft = new Vector2();
            this.topRightDirty = true;
            this.topRight = new Vector2();

            if (updateAll)
            {
                this.updateSize();
                this.updateTopRight();
                this.updateBottomLeft();
                this.updateCenter();
            }
        }

        /// <summary>
        /// Initializes a new instance of the RectangleF struct.
        /// </summary>
        /// <param name="topLeft">Vector2 containing the x- and y-coordinate of the rectangle.</param>
        /// <param name="size">Vector2 containing the size (width and height) of the rectangle.</param>
        public RectangleF(Vector2 topLeft, Vector2 size)
            : this(topLeft.X, topLeft.Y, size.X, size.Y)
        {
        }

        /// <summary>
        /// Initializes a new instance of the RectangleF struct.
        /// </summary>
        /// <param name="topLeft">Vector2 containing the x- and y-coordinate of the rectangle.</param>
        /// <param name="width">Width of the rectangle.</param>
        /// <param name="height">Height of the rectangle.</param>
        public RectangleF(Vector2 topLeft, float width, float height)
            : this(topLeft.X, topLeft.Y, width, height)
        {
        }

        /// <summary>
        /// Initializes a new instance of the RectangleF struct.
        /// </summary>
        /// <param name="rect">The Rectangle to construct this RectangleF from</param>
        public RectangleF(Rectangle rect)
            : this(rect.X, rect.Y, rect.Width, rect.Height)
        {
        }

        /// <summary>
        /// Initializes a new instance of the RectangleF struct.
        /// </summary>
        /// <param name="rect">The OrientedRectangleF to construct this RectangleF from</param>
        public RectangleF(OrientedRectangleF rect)
            : this(rect.X, rect.Y, rect.Width, rect.Height)
        {
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the y-coordinate of the bottom of the rectangle.
        /// </summary>
        public float Bottom
        {
            get { return this.bottomRight.Y; }
        }

        /// <summary>
        /// Gets a value indicating whether the RectangleF is empty.
        /// </summary>
        public bool IsEmpty
        {
            get 
            { 
                return this.topLeft.X == 0.0f && 
                       this.topLeft.Y == 0.0f && 
                       this.bottomRight.X == 0.0f && 
                       this.bottomRight.Y == 0.0f; 
            }
        }

        /// <summary>
        /// Gets the x-coordinate of the left side of the rectangle.
        /// </summary>
        public float Left
        {
            get { return this.topLeft.X; }
        }

        /// <summary>
        /// Gets or sets the x-coordinate of the left side of the rectangle.
        /// </summary>
        public float X
        {
            get 
            { 
                return this.topLeft.X; 
            }

            set
            {
                if (this.topLeft.X != value)
                {
                    this.topLeft.X = value;
                    this.bottomLeft.X = value;
                    this.topRight.X = this.topLeft.X + this.size.X;
                    this.center.X = (this.topRight.X + this.topLeft.X) * 0.5f;
                }
            }
        }

        /// <summary>
        /// Gets or sets the y-coordinate of the top side of the rectangle.
        /// </summary>
        public float Y
        {
            get 
            { 
                return this.topLeft.Y; 
            }

            set
            {
                if (this.topLeft.Y != value)
                {
                    this.topLeft.Y = value;
                    this.bottomLeftDirty = true;
                    this.topRightDirty = true;
                    this.sizeDirty = true;
                    this.centerDirty = true;
                }
            }
        }

        public float Width
        {
            get 
            {
                if (this.sizeDirty)
                {
                    this.updateSize();
                }

                return this.size.X;
            }

            set
            {
                if (this.size.X != value)
                {
                    this.bottomRight.X = this.topLeft.X + value;
                    this.topRightDirty = true;
                    this.bottomLeftDirty = true;
                    this.sizeDirty = true;
                    this.centerDirty = true;
                }
            }
        }

        public float Height
        {
            get
            {
                if (this.sizeDirty)
                {
                    this.updateSize();
                }

                return this.size.Y;
            }

            set
            {
                if (this.size.Y != value)
                {
                    this.bottomRight.Y = this.topLeft.Y + value;
                    this.topRightDirty = true;
                    this.bottomLeftDirty = true;
                    this.sizeDirty = true;
                    this.centerDirty = true;
                }
            }
        }

        /// <summary>
        /// Gets or sets the upper-left value of the RectangleF.
        /// </summary>
        public Vector2 Location
        {
            get { return this.topLeft; }
            set { this.TopLeft = value; }
        }

        public Vector2 TopLeft
        {
            get 
            { 
                return this.topLeft; 
            }

            set
            {
                if (this.topLeft != value)
                {
                    this.topLeft = value;
                    this.bottomRight = value + this.size;
                    this.center = (this.bottomRight + this.topLeft) * 0.5f;
                    this.bottomLeft = new Vector2(this.topLeft.X, this.bottomRight.Y);
                    this.topRight = new Vector2(this.bottomRight.X, this.topLeft.Y);
                    this.bottomLeftDirty = false;
                    this.topRightDirty = false;
                    this.centerDirty = false;
                }
            }
        }

        public Vector2 TopRight
        {
            get
            {
                if (this.topRightDirty)
                {
                    this.updateTopRight();
                }

                return this.topRight;
            }

            set
            {
                this.topRightDirty = false;   
                this.topRight = value;
                this.topLeft = new Vector2(this.topLeft.X, value.Y);
                this.bottomRight = new Vector2(value.X, this.bottomRight.Y);

                this.bottomLeftDirty = false; 
                this.bottomLeft = new Vector2(this.topLeft.X, this.bottomRight.Y);

                this.sizeDirty = true;
            }
        }

        public Vector2 BottomLeft
        {
            get
            {
                if (this.bottomLeftDirty)
                {
                    this.updateBottomLeft();
                }

                return this.bottomLeft;
            }

            set
            {
                this.topLeft = new Vector2(value.X, this.topLeft.Y);
                this.topRightDirty = false;   
                this.topRight = new Vector2(this.bottomRight.X, this.topLeft.Y);
                this.bottomRight = new Vector2(this.bottomRight.X, value.Y);
                this.bottomLeftDirty = false; 
                this.bottomLeft = value;
                this.sizeDirty = true;
            }
        }

        public Vector2 BottomRight
        {
            get 
            { 
                return this.bottomRight; 
            }

            set
            {
                if (this.bottomRight != value)
                {
                    this.bottomRight = value;
                    this.topRightDirty = true;
                    this.bottomLeftDirty = true;
                    this.sizeDirty = true;
                    this.centerDirty = true;
                }
            }
        }

        public Vector2 Size
        {
            get
            {
                if (this.sizeDirty)
                {
                    this.updateSize();
                }

                return this.size;
            }

            set
            {
                if (this.Size != value)
                {
                    this.size = value;
                    this.sizeDirty = false;
                    this.BottomRight = this.topLeft + value;
                }
            }
        }

        /// <summary>
        /// Gets or sets the Vector2 that specifies the center of the rectangle.
        /// </summary>
        public Vector2 Center
        {
            get
            {
                if (this.centerDirty)
                {
                    this.updateCenter();
                }

                return this.center;
            }

            set
            {
                if (this.center.X != value.X ||
                    this.center.Y != value.Y)
                {
                    if (this.sizeDirty)
                    {
                        this.updateSize();
                    }

                    Vector2 halfSize = this.size * 0.5f;

                    float vX = value.X;
                    float vY = value.Y;
                    float valXhsX = vX - halfSize.X;
                    float valXhsX2 = vX + halfSize.X;
                    float valYhsY = vY - halfSize.Y;
                    float valYhsY2 = vY + halfSize.Y;

                    this.topLeft.X = valXhsX;
                    this.topLeft.Y = valYhsY;

                    this.topRight.X = valXhsX2;
                    this.topRight.Y = valYhsY;
                    this.topRightDirty = false;

                    this.bottomRight.X = valXhsX2;
                    this.bottomRight.Y = valYhsY2;

                    this.bottomLeft.X = valXhsX;
                    this.bottomLeft.Y = valYhsY2;
                    this.bottomLeftDirty = false;

                    this.center.X = vX;
                    this.center.Y = vY;
                    this.centerDirty = false;
                }
            }
        }

        /// <summary>
        /// Gets the x-coordinate of the right side of the rectangle.
        /// </summary>
        public float Right
        {
            get { return this.bottomRight.X; }
        }

        /// <summary>
        /// Gets the y-coordinate of the top of the rectangle.
        /// </summary>
        public float Top
        {
            get { return this.topLeft.Y; }
        }

        public Vector2[] Corners
        {
            get
            {
                return new Vector2[] { this.TopLeft, this.TopRight, this.BottomRight, this.BottomLeft };
            }
        }

        public IEnumerable<Vector2> CornerEnumerable
        {
            get
            {
                yield return this.TopLeft;
                yield return this.TopRight;
                yield return this.BottomRight;
                yield return this.BottomLeft;
            }
        }

        #endregion

        #region Methods
        /// <summary>
        /// Creates a RectangleF defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">The first RectangleF to compare.</param>
        /// <param name="value2">The second RectangleF to compare.</param>
        /// <returns>The area where the two parameters overlap.</returns>
        /// <remarks>If the two rectangles do not overlap, the resulting RectangleF will be empty.</remarks>
        public static RectangleF Intersect(RectangleF value1, RectangleF value2)
        {
            float right1 = value1.X + value1.Width;
            float right2 = value2.X + value2.Width;
            float bottom1 = value1.Y + value1.Height;
            float bottom2 = value2.Y + value2.Height;
            float maxLeft = (value1.X > value2.X) ? value1.X : value2.X;
            float maxTop = (value1.Y > value2.Y) ? value1.Y : value2.Y;
            float minRight = (right1 < right2) ? right1 : right2;
            float minBottom = (bottom1 < bottom2) ? bottom1 : bottom2;

            if ((minRight > maxLeft) && (minBottom > maxTop))
            {
                RectangleF rectangle = new RectangleF();

                rectangle.X = maxLeft;
                rectangle.Y = maxTop;
                rectangle.Width = minRight - maxLeft;
                rectangle.Height = minBottom - maxTop;

                return rectangle;
            }

            return RectangleF.Empty;
        }

        /// <summary>
        /// Creates a RectangleF defining the area where one rectangle overlaps with another rectangle.
        /// </summary>
        /// <param name="value1">The first RectangleF to compare.</param>
        /// <param name="value2">The second RectangleF to compare.</param>
        /// <param name="result">The area where the two parameters overlap.</param>
        /// <remarks>If the two rectangles do not overlap, the resulting RectangleF will be empty.</remarks>
        public static void Intersect(ref RectangleF value1, ref RectangleF value2, out RectangleF result)
        {
            result = Intersect(value1, value2);
        }

        /// <summary>
        /// Clips one rectangle to the size of the other.
        /// </summary>
        /// <param name="rect">Rectangle to be clipped.</param>
        /// <param name="clipRect">Clipping rectangle (maximum bounds)</param>
        /// <returns>The clipped Rectangle</returns>
        public static RectangleF Clip(RectangleF rect, RectangleF clipRect)
        {
            float left = MathHelper.Clamp(rect.Left, clipRect.Left, clipRect.Right);
            float top = MathHelper.Clamp(rect.Top, clipRect.Top, clipRect.Bottom);
            float bottom = MathHelper.Clamp(rect.Bottom, clipRect.Top, clipRect.Bottom);
            float right = MathHelper.Clamp(rect.Right, clipRect.Left, clipRect.Right);

            return new RectangleF(left, top, right - left, bottom - top);
        }

        /// <summary>
        /// Compares two rectangles for equality.
        /// </summary>
        /// <param name="first">first rectangle.</param>
        /// <param name="second">second rectangle.</param>
        /// <returns>true if the rectangles are equal; false otherwise.</returns>
        public static bool operator ==(RectangleF first, RectangleF second)
        {
            return first.X == second.X && 
                   first.Y == second.Y && 
                   first.Width == second.Width && 
                   first.Height == second.Height;
        }

        /// <summary>
        /// Compares two rectangles for inequality.
        /// </summary>
        /// <param name="first">first rectangle.</param>
        /// <param name="second">second rectangle.</param>
        /// <returns>true if the rectangles are not equal; false otherwise.</returns>
        public static bool operator !=(RectangleF first, RectangleF second)
        {
            return first.X != second.X || 
                   first.Y != second.Y || 
                   first.Width != second.Width || 
                   first.Height != second.Height;
        }

        public static RectangleF Fromstring(string s)
        {
            float x = 0f, y = 0f, w = 0f, h = 0f;

            string[] parts = s.Split(new char[] { ':', ' ' });

#if WINDOWS
            if (float.TryParse(parts[1], out x) &&
                float.TryParse(parts[3], out y) &&
                float.TryParse(parts[5], out w) &&
                float.TryParse(parts[7].Replace('}', ' ').Trim(), out h))
            {
                return new RectangleF(x, y, w, h);
            }
            else
            {
                throw new FormatException(string.Format("invalid RectangleF string format: {0}", s));
            }
#else
            try 
            { 
                x = float.Parse(parts[1]); 
                y = float.Parse(parts[3]); 
                w = float.Parse(parts[5]); 
                h = float.Parse(parts[7]); 
            } 
            catch 
            { 
                throw new FormatException(string.Format("invalid RectangleF string format: {0}", s));
            }

            return new RectangleF(x, y, w, h);
#endif
        }

        /// <summary>
        /// Creates a new RectangleF that exactly contains two other rectangles. 
        /// </summary>
        /// <param name="value1">The first RectangleF to contain.</param>
        /// <param name="value2">The second RectangleF to contain.</param>
        /// <returns>The union of the two RectangleF parameters.</returns>
        public static RectangleF Union(RectangleF value1, RectangleF value2)
        {
            if (value1.IsEmpty)
            {
                return value2;
            }

            if (value2.IsEmpty)
            {
                return value1;
            }

            RectangleF rectangle = new RectangleF();
            float right1 = value1.X + value1.Width;
            float right2 = value2.X + value2.Width;
            float bottom1 = value1.Y + value1.Height;
            float bottom2 = value2.Y + value2.Height;
            float minLeft = (value1.X < value2.X) ? value1.X : value2.X;
            float minTop = (value1.Y < value2.Y) ? value1.Y : value2.Y;
            float maxRight = (right1 > right2) ? right1 : right2;
            float maxBottom = (bottom1 > bottom2) ? bottom1 : bottom2;

            rectangle.X = minLeft;
            rectangle.Y = minTop;
            rectangle.Width = maxRight - minLeft;
            rectangle.Height = maxBottom - minTop;

            return rectangle;
        }

        public static RectangleF FromPoints(params Vector2[] points)
        {
            float minX = float.MaxValue;
            float minY = float.MaxValue;
            float maxX = float.MinValue;
            float maxY = float.MinValue;

            foreach (Vector2 p in points)
            {
                minX = Math.Min(minX, p.X);
                minY = Math.Min(minY, p.Y);
                maxX = Math.Max(maxX, p.X);
                maxY = Math.Max(maxY, p.Y);
            }

            RectangleF newRct = new RectangleF(minX, minY, maxX - minX, maxY - minY, true);

            return newRct;
        }

        /// <summary>
        /// Creates a new RectangleF that exactly contains two other rectangles. 
        /// </summary>
        /// <param name="value1">The first RectangleF to contain.</param>
        /// <param name="value2">The second RectangleF to contain.</param>
        /// <param name="result">The union of the two RectangleF parameters.</param>
        public static void Union(ref RectangleF value1, ref RectangleF value2, out RectangleF result)
        {
            result = Union(value1, value2);
        }

        /// <summary>
        /// Determines whether this RectangleF contains a specified point represented by its x- and y-coordinates.
        /// </summary>
        /// <param name="x">The x-coordinate of the specified point.</param>
        /// <param name="y">The y-coordinate of the specified point.</param>
        /// <returns>true if the specified point is contained within this RectangleF; false otherwise.</returns>
        public bool Contains(float x, float y)
        {
            // TODO: optimize, don't calculate right and bottom, get it from BottomRight
            return (x >= this.X && y >= this.Y) && 
                   (x < this.X + this.Width) && (y < this.Y + this.Height);
        }

        /// <summary>
        /// Determines whether this RectangleF contains a specified Point.
        /// </summary>
        /// <param name="value">The Point to evaluate.</param>
        /// <returns>true if the specified Point is contained within this RectangleF; false otherwise.</returns>
        public bool Contains(Point value)
        {
            return this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this RectangleF contains a specified Vector2.
        /// </summary>
        /// <param name="value">The Vector2 to evaluate.</param>
        /// <returns>true if the specified Vector2 is contained within this RectangleF; false otherwise.</returns>
        public bool Contains(Vector2 value)
        {
            return this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this RectangleF contains a specified Point.
        /// </summary>
        /// <param name="value">The Point to evaluate.</param>
        /// <param name="result">true if the specified Point is contained within this Rectangle; false otherwise.</param>
        public void Contains(ref Point value, out bool result)
        {
            result = this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this RectangleF contains a specified Vector2.
        /// </summary>
        /// <param name="value">The Vector2 to evaluate.</param>
        /// <param name="result">true if the specified Vector2 is contained within this Rectangle; false otherwise.</param>
        public void Contains(ref Vector2 value, out bool result)
        {
            result = this.Contains(value.X, value.Y);
        }

        /// <summary>
        /// Determines whether this RectangleF entirely contains a specified RectangleF.
        /// </summary>
        /// <param name="value">The RectangleF to evaluate. </param>
        /// <returns>true if this RectangleF entirely contains the specified RectangleF; false otherwise.</returns>
        public bool Contains(RectangleF value)
        {
            // TODO: optimize, don't calculate right and bottom, get it from BottomRight
            return (value.X >= this.X) && 
                   (value.Y >= this.Y) && 
                   ((value.X + value.Width) <= (this.X + this.Width)) && 
                   ((value.Y + value.Height) <= (this.Y + this.Height));
        }

        /// <summary>
        /// Determines whether this RectangleF entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <returns>true if this RectangleF entirely contains the specified Rectangle; false otherwise.</returns>
        public bool Contains(Rectangle value)
        {
            // TODO: optimize, don't calculate right and bottom, get it from BottomRight
            return (value.X >= this.X) && 
                   (value.Y >= this.Y) && 
                   ((value.X + value.Width) <= (this.X + this.Width)) && 
                   ((value.Y + this.Height) <= (value.Y + this.Height));
        }

        /// <summary>
        /// Determines whether this RectangleF entirely contains a specified RectangleF.
        /// </summary>
        /// <param name="value">The RectangleF to evaluate.</param>
        /// <param name="result">On exit, is true if this RectangleF entirely contains the specified RectangleF, or false if not.</param>
        public void Contains(ref RectangleF value, out bool result)
        {
            result = this.Contains(value);
        }

        /// <summary>
        /// Determines whether this RectangleF entirely contains a specified Rectangle.
        /// </summary>
        /// <param name="value">The Rectangle to evaluate.</param>
        /// <param name="result">On exit, is true if this RectangleF entirely contains the specified Rectangle, or false if not.</param>
        public void Contains(ref Rectangle value, out bool result)
        {
            result = this.Contains(value);
        }

        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified RectangleF.
        /// </summary>
        /// <param name="other">The RectangleF to compare with the current RectangleF.</param>
        /// <returns>true if the specified RectangleF is equal to the current RectangleF; false otherwise.</returns>
        public bool Equals(RectangleF other)
        {
            return this.X == other.X && 
                   this.Y == other.Y && 
                   this.Width == other.Width && 
                   this.Height == other.Height;
        }

        /// <summary>
        /// Returns a value that indicates whether the current instance is equal to a specified object.
        /// </summary>
        /// <param name="obj">The Object to compare with the current RectangleF.</param>
        /// <returns>true if the specified object is equal to the current RectangleF; false otherwise.</returns>
        public override bool Equals(object obj)
        {
            if (obj is RectangleF)
            {
                return this.Equals((RectangleF)obj);
            }

            return false;
        }

        /// <summary>
        /// Gets the hash code for this object.
        /// </summary>
        /// <returns>Hash code for this object.</returns>
        public override int GetHashCode()
        {
            return this.X.GetHashCode() + 
                   this.Y.GetHashCode() + 
                   this.Width.GetHashCode() + 
                   this.Height.GetHashCode();
        }

        /// <summary>
        /// Pushes the edges of the RectangleF out by the horizontal and vertical values specified.
        /// </summary>
        /// <param name="horizontalAmount">Value to push the sides out by.</param>
        /// <param name="verticalAmount">Value to push the top and bottom out by.</param>
        /// <remarks>Each corner of the RectangleF is pushed away from the center of the 
        /// rectangle by the specified amounts. This results in the width and height of 
        /// the RectangleF increasing by twice the values provided.</remarks>
        public void Inflate(float horizontalAmount, float verticalAmount)
        {
            Vector2 topLeft = new Vector2();
            topLeft.X = this.X - horizontalAmount;
            topLeft.Y = this.Y - verticalAmount;

            Vector2 bottomRight = new Vector2();
            bottomRight.X = (this.X + this.Width) + horizontalAmount;
            bottomRight.Y = (this.Y + this.Height) + verticalAmount;

            this.TopLeft = topLeft;
            this.BottomRight = bottomRight;
        }

        /// <summary>
        /// Determines whether a specified RectangleF intersects with this RectangleF. 
        /// </summary>
        /// <param name="value">The RectangleF to evaluate.</param>
        /// <returns>true if the specified RectangleF intersects with this one; false otherwise.</returns>
        public bool Intersects(RectangleF value)
        {
            float x1 = value.X;
            float x2 = this.X;
            float y1 = value.Y;
            float y2 = this.Y;

            // TODO: optimize, don't calculate right and bottom, get it from BottomRight
            return x1 < (x2 + this.Width) &&
                   x2 < (x1 + value.Width) && 
                   y1 < (y2 + this.Height) && 
                   y2 < (y1 + value.Height);
        }

        /// <summary>
        /// Determines whether a specified RectangleF intersects with this RectangleF. 
        /// </summary>
        /// <param name="value">The RectangleF to evaluate.</param>
        /// <param name="result">true if the specified RectangleF intersects with this one; false otherwise.</param>
        public void Intersects(ref RectangleF value, out bool result)
        {
            result = this.Intersects(value);
        }

        /// <summary>
        /// Determines whether a specified CircleF intersects with this RectangleF. 
        /// </summary>
        /// <param name="value">The CircleF to evaluate.</param>
        /// <returns>true if the specified CircleF intersects with this RectangleF; false otherwise.</returns>
        public bool Intersects(CircleF value)
        {
            return value.Intersects(this);
        }

        /// <summary>
        /// Determines whether a specified CircleF intersects with this RectangleF. 
        /// </summary>
        /// <param name="value">The CircleF to evaluate.</param>
        /// <param name="result">true if the specified CircleF intersects with this RectangleF; false otherwise.</param>
        public void Intersects(ref CircleF value, out bool result)
        {
            value.Intersects(ref this, out result);
        }

        /// <summary>
        /// Changes the position of the RectangleF.
        /// </summary>
        /// <param name="offsetX">Change in the x-position.</param>
        /// <param name="offsetY">Change in the y-position.</param>
        public void Offset(float offsetX, float offsetY)
        {
            this.Location += new Vector2(offsetX, offsetY);
        }

        /// <summary>
        /// Changes the position of the RectangleF.
        /// </summary>
        /// <param name="offsetX">Change in the x-position.</param>
        /// <param name="offsetY">Change in the y-position.</param>
        public void Offset(int offsetX, int offsetY)
        {
            this.Location += new Vector2(offsetX, offsetY);
        }

        /// <summary>
        /// Changes the position of the RectangleF.
        /// </summary>
        /// <param name="amount">The values to adjust the position of the RectangleF by.</param>
        public void Offset(Vector2 amount)
        {
            this.Location += amount;
        }

        /// <summary>
        /// Changes the position of the RectangleF.
        /// </summary>
        /// <param name="amount">The values to adjust the position of the RectangleF by.</param>
        public void Offset(Point amount)
        {
            this.Location += new Vector2(amount.X, amount.Y);
        }

        /// <summary>
        /// Retrieves a string representation of the current object.
        /// </summary>
        /// <returns>string that represents the object.</returns>
        public override string ToString()
        {
            CultureInfo currentCulture = CultureInfo.InvariantCulture;

            return string.Format(
                                 currentCulture, 
                                 "{{X:{0} Y:{1} Width:{2} Height:{3}}}",
                                 this.X.ToString(currentCulture),
                                 this.Y.ToString(currentCulture),
                                 this.Width.ToString(currentCulture),
                                 this.Height.ToString(currentCulture));
        }

        public void Scale(float factor)
        {
            this.Scale(new Vector2(factor, factor));
        }

        public void Scale(Vector2 factor)
        {
            float oldWidth = this.Width;
            float width = this.Width * factor.X;

            float oldHeight = this.Height;
            float height = this.Height * factor.Y;

            this.X -= (height - oldHeight) * 0.5f;
            this.Y -= (width - oldWidth) * 0.5f;
            this.Width = width;
            this.Height = height;
        }

        public RectangleF Round()
        {
            return new RectangleF(this.Location.Round(), this.Size.Round());
        }

        public Rectangle ToRectangle()
        {
            return new Rectangle(
                                 this.X.ToNearestInt(), 
                                 this.Y.ToNearestInt(), 
                                 this.Width.ToNearestInt(), 
                                 this.Height.ToNearestInt());
        }

        public void ToRectangle(out Rectangle rect)
        {
#if WINDOWS
            rect.X = this.X.ToNearestInt();
            rect.Y = this.Y.ToNearestInt();
            rect.Width = this.Width.ToNearestInt();
            rect.Height = this.Height.ToNearestInt();
#else
            rect = new Rectangle(
                                 this.X.ToNearestInt(), 
                                 this.Y.ToNearestInt(), 
                                 this.Width.ToNearestInt(), 
                                 this.Height.ToNearestInt());
#endif
        }

        public OrientedRectangleF ToOrientedRectangleF()
        {
            return new OrientedRectangleF(this);
        }

        #endregion

        #region Helpers
        private void updateSize()
        {
            this.size.X = this.BottomRight.X - this.TopLeft.X;
            this.size.Y = this.BottomRight.Y - this.TopLeft.Y;
            this.sizeDirty = false;
        }

        private void updateBottomLeft()
        {
            this.bottomLeft.X = this.topLeft.X;
            this.bottomLeft.Y = this.bottomRight.Y;
            this.bottomLeftDirty = false;
        }

        private void updateTopRight()
        {
            this.topRight.X = this.BottomRight.X;
            this.topRight.Y = this.TopLeft.Y;
            this.topRightDirty = false;
        }

        private void updateCenter()
        {
            this.center.X = this.TopLeft.X + ((this.BottomRight.X - this.TopLeft.X) * 0.5f);
            this.center.Y = this.TopLeft.Y + ((this.BottomRight.Y - this.TopLeft.Y) * 0.5f);
            this.centerDirty = false;
        }

        #endregion
    }
}
